export type EaseInOutOption = {
  // 是否在实例化时运行
  autoplay?: boolean
  // 开始运行时的数字
  startVal: number
  // 运行结束时的数字
  endVal: number
  // 运行时长
  duration?: number
  // 是否匀速
  useEasing?: boolean
  // 是否自定义数字格式
  isFormat?: boolean
  /**保留多少位小数 */
  decimals?: number
  // 小数位字符 （默认：.）
  decimal?: string
  /** 千分位符号*/
  separator?: string
  // 数字前缀
  prefix?: string
  // 数字后缀
  suffix?: string
  // 每次加载的回调函数
  running?: (value: string) => void
  // 加载结束的回调
  finished?: (value: any) => void
  /**曲线公式 */
  easingFn?: (t: number, b: number, c: number, d: number) => number
}

class EaseInOut {
  startVal = 0
  endVal = 0
  displayValue = 0
  printVal = 0
  duration = 3000
  startTime = 0
  rAF = 0
  useEasing = true
  isFormat = false
  decimals = 0
  decimal = '.'
  separator = ''
  prefix = ''
  suffix = ''

  // 变速函数
  easingFn = (t: number, b: number, c: number, d: number) => {
    return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
  }

  running = (value: string) => {}
  finished = (value: any) => {}

  constructor(data?: EaseInOutOption) {
    data && this.init(data)
  }

  // 初始化
  init(data: EaseInOutOption) {
    data.startVal && (this.startVal = data.startVal)
    data.endVal && (this.endVal = data.endVal)
    data.duration && (this.duration = data.duration)
    data.useEasing && (this.useEasing = data.useEasing)
    data.isFormat && (this.isFormat = data.isFormat)
    data.decimals && (this.decimals = data.decimals)
    data.decimal && (this.decimal = data.decimal)
    data.separator && (this.separator = data.separator)
    data.prefix && (this.prefix = data.prefix)
    data.suffix && (this.suffix = data.suffix)
    data.running && (this.running = data.running)
    data.finished && (this.finished = data.finished)
    data.easingFn && (this.easingFn = data.easingFn)
    data.autoplay && this.start()
  }

  getRAF() {
    return this.rAF
  }

  // 开始运行
  start() {
    this.rAF = requestAnimationFrame(timestamp => this.count(timestamp))
  }
  // 停止运行
  stop() {
    cancelAnimationFrame(this.rAF)
  }

  count(timestamp: number) {
    if (!this.startTime) this.startTime = timestamp

    const progress = timestamp - this.startTime

    if (this.useEasing) {
      if (this.startVal > this.endVal) {
        this.printVal =
          this.startVal - this.easingFn(progress, 0, this.startVal - this.endVal, this.duration)
      } else {
        this.printVal = this.easingFn(
          progress,
          this.startVal,
          this.endVal - this.startVal,
          this.duration
        )
      }
    } else {
      if (this.startVal > this.endVal) {
        this.printVal = this.startVal - (this.startVal - this.endVal) * (progress / this.duration)
      } else {
        this.printVal = this.startVal + (this.endVal - this.startVal) * (progress / this.duration)
      }
    }

    if (this.startVal > this.endVal) {
      this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal
    } else {
      this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal
    }

    const displayValue = this.formatNumber(this.printVal)
    this.displayValue = displayValue
    const runningCb = this.running(displayValue)

    if (runningCb !== undefined) {
      this.stop()
    } else if (progress < this.duration) {
      this.rAF = requestAnimationFrame(timestamp => this.count(timestamp))
    } else {
      this.finished(displayValue)
      this.stop()
    }
  }

  isNumber(val: any) {
    return !isNaN(parseFloat(val))
  }

  formatNumber(num: any) {
    num = num.toFixed(this.decimals)
    if (!this.isFormat) return num
    num += ''
    const x = num.split('.')
    let x1 = x[0]
    const x2 = x.length > 1 ? this.decimal + x[1] : ''
    const rgx = /(\d+)(\d{3})/
    if (this.separator && !this.isNumber(this.separator)) {
      while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + this.separator + '$2')
      }
    }
    return this.prefix + x1 + x2 + this.suffix
  }
}

export default EaseInOut
